import * as Cesium from "cesium";
import Sandcastle from "Sandcastle";

const viewer = new Cesium.Viewer("cesiumContainer", {
  shouldAnimate: true,
});
const planePosition = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883, 800.0);
const particlesOffset = new Cesium.Cartesian3(
  -8.950115473940969,
  34.852766731753945,
  -30.235411095432937,
);
const cameraLocation = Cesium.Cartesian3.add(
  planePosition,
  particlesOffset,
  new Cesium.Cartesian3(),
);
const resetCamera = function () {
  viewer.camera.lookAt(cameraLocation, new Cesium.Cartesian3(-450, -300, 200));
};
resetCamera();

// Draw particle image to a canvas
let particleCanvas;
function getImage() {
  if (!Cesium.defined(particleCanvas)) {
    particleCanvas = document.createElement("canvas");
    particleCanvas.width = 20;
    particleCanvas.height = 20;
    const context2D = particleCanvas.getContext("2d");
    context2D.beginPath();
    context2D.arc(8, 8, 8, 0, Cesium.Math.TWO_PI, true);
    context2D.closePath();
    context2D.fillStyle = "rgb(255, 255, 255)";
    context2D.fill();
  }
  return particleCanvas;
}

// Add plane to scene
const hpr = new Cesium.HeadingPitchRoll(0.0, Cesium.Math.PI_OVER_TWO, 0.0);
const orientation = Cesium.Transforms.headingPitchRollQuaternion(
  planePosition,
  hpr,
);
viewer.entities.add({
  model: {
    uri: "../../SampleData/models/CesiumAir/Cesium_Air.glb",
    scale: 3.5,
  },
  position: planePosition,
  orientation: orientation,
});

// creating particles model matrix
const translationOffset = Cesium.Matrix4.fromTranslation(
  particlesOffset,
  new Cesium.Matrix4(),
);
const translationOfPlane = Cesium.Matrix4.fromTranslation(
  planePosition,
  new Cesium.Matrix4(),
);
const particlesModelMatrix = Cesium.Matrix4.multiplyTransformation(
  translationOfPlane,
  translationOffset,
  new Cesium.Matrix4(),
);

// creating the particle systems
const rocketOptions = {
  numberOfSystems: 50.0,
  iterationOffset: 0.1,
  cartographicStep: 0.000001,
  baseRadius: 0.0005,

  colorOptions: [
    {
      minimumRed: 1.0,
      green: 0.5,
      minimumBlue: 0.05,
      alpha: 1.0,
    },
    {
      red: 0.9,
      minimumGreen: 0.6,
      minimumBlue: 0.01,
      alpha: 1.0,
    },
    {
      red: 0.8,
      green: 0.05,
      minimumBlue: 0.09,
      alpha: 1.0,
    },
    {
      minimumRed: 1,
      minimumGreen: 0.05,
      blue: 0.09,
      alpha: 1.0,
    },
  ],
};

const cometOptions = {
  numberOfSystems: 100.0,
  iterationOffset: 0.003,
  cartographicStep: 0.0000001,
  baseRadius: 0.0005,

  colorOptions: [
    {
      red: 0.6,
      green: 0.6,
      blue: 0.6,
      alpha: 1.0,
    },
    {
      red: 0.6,
      green: 0.6,
      blue: 0.9,
      alpha: 0.9,
    },
    {
      red: 0.5,
      green: 0.5,
      blue: 0.7,
      alpha: 0.5,
    },
  ],
};

let scratchCartesian3 = new Cesium.Cartesian3();
let scratchCartographic = new Cesium.Cartographic();
const forceFunction = function (options, iteration) {
  return function (particle, dt) {
    dt = Cesium.Math.clamp(dt, 0.0, 0.05);

    scratchCartesian3 = Cesium.Cartesian3.normalize(
      particle.position,
      new Cesium.Cartesian3(),
    );
    scratchCartesian3 = Cesium.Cartesian3.multiplyByScalar(
      scratchCartesian3,
      -40.0 * dt,
      scratchCartesian3,
    );

    scratchCartesian3 = Cesium.Cartesian3.add(
      particle.position,
      scratchCartesian3,
      scratchCartesian3,
    );

    scratchCartographic = Cesium.Cartographic.fromCartesian(
      scratchCartesian3,
      Cesium.Ellipsoid.WGS84,
      scratchCartographic,
    );

    const angle = (Cesium.Math.PI * 2.0 * iteration) / options.numberOfSystems;
    iteration += options.iterationOffset;
    scratchCartographic.longitude +=
      Math.cos(angle) * options.cartographicStep * 30.0 * dt;
    scratchCartographic.latitude +=
      Math.sin(angle) * options.cartographicStep * 30.0 * dt;

    particle.position = Cesium.Cartographic.toCartesian(scratchCartographic);
  };
};

const matrix4Scratch = new Cesium.Matrix4();
let scratchAngleForOffset = 0.0;
const scratchOffset = new Cesium.Cartesian3();
const imageSize = new Cesium.Cartesian2(15.0, 15.0);
function createParticleSystems(options, systemsArray) {
  const length = options.numberOfSystems;
  for (let i = 0; i < length; ++i) {
    scratchAngleForOffset = (Math.PI * 2.0 * i) / options.numberOfSystems;
    scratchOffset.x += options.baseRadius * Math.cos(scratchAngleForOffset);
    scratchOffset.y += options.baseRadius * Math.sin(scratchAngleForOffset);

    const emitterModelMatrix = Cesium.Matrix4.fromTranslation(
      scratchOffset,
      matrix4Scratch,
    );
    const color = Cesium.Color.fromRandom(
      options.colorOptions[i % options.colorOptions.length],
    );
    const force = forceFunction(options, i);

    const item = viewer.scene.primitives.add(
      new Cesium.ParticleSystem({
        image: getImage(),
        startColor: color,
        endColor: color.withAlpha(0.0),
        particleLife: 3.5,
        speed: 0.00005,
        imageSize: imageSize,
        emissionRate: 30.0,
        emitter: new Cesium.CircleEmitter(0.1),
        lifetime: 0.1,
        updateCallback: force,
        modelMatrix: particlesModelMatrix,
        emitterModelMatrix: emitterModelMatrix,
      }),
    );
    systemsArray.push(item);
  }
}

const rocketSystems = [];
const cometSystems = [];
createParticleSystems(rocketOptions, rocketSystems);
createParticleSystems(cometOptions, cometSystems);

// toolbar elements
function showAll(systemsArray, show) {
  const length = systemsArray.length;
  for (let i = 0; i < length; ++i) {
    systemsArray[i].show = show;
  }
}

const options = [
  {
    text: "Comet Tail",
    onselect: function () {
      showAll(rocketSystems, false);
      showAll(cometSystems, true);
      resetCamera();
    },
  },
  {
    text: "Rocket Thruster",
    onselect: function () {
      showAll(cometSystems, false);
      showAll(rocketSystems, true);
      resetCamera();
    },
  },
];
Sandcastle.addToolbarMenu(options);
showAll(cometSystems, true);
